/*++

Copyright (c) 1990-2000  Microsoft Corporation

Module Name:

    queue.c

Abstract:

    This is a C version of a very simple sample driver that illustrates
    how to use the driver framework and demonstrates best practices.

--*/

#include "driver.h"
#define TRUE 1
#define FALSE 0
#define true 1
#define false 0
#include "parallel.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, PortIOQueueInitialize)
#pragma alloc_text (PAGE, PortIOEvtIoDeviceControl)
#endif

#pragma warning( disable : 4100 ) 
#pragma warning( disable : 4305 ) 
   
#define outportb(ADDR,BYTE) outp(ADDR,BYTE)   

NTSTATUS
PortIOQueueInitialize(
    __in WDFDEVICE Device
    )
/*++

Routine Description:


     The I/O dispatch callbacks for the frameworks device object
     are configured in this function.

     A single default I/O Queue is configured for serial request
     processing, and a driver context memory allocation is created
     to hold our structure QUEUE_CONTEXT.

     This memory may be used by the driver automatically synchronized
     by the Queue's presentation lock.

     The lifetime of this memory is tied to the lifetime of the I/O
     Queue object, and we register an optional destructor callback
     to release any private allocations, and/or resources.


Arguments:

    Device - Handle to a framework device object.

Return Value:

    VOID

--*/
{
    WDFQUEUE queue;
    NTSTATUS status;
    WDF_IO_QUEUE_CONFIG    queueConfig;

    PAGED_CODE();

    //
    // Configure a default queue so that requests that are not
    // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
    // other queues get dispatched here.
    //
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
        &queueConfig,
        WdfIoQueueDispatchSequential
        );

    // 
    // By registering the IOCTL handler for the default queue,
    // the default queue will fail all other requests
    //
    queueConfig.EvtIoDeviceControl = PortIOEvtIoDeviceControl;    

    //
    // NOTE: If you need to have the queue's callbacks synchronized 
    // with one another then you should set the WDF_SYNCHRONIZATION_SCOPE
    // enum (member of WDF_OBJECT_ATTRIBUTES) to WdfSynchronizationScopeQueue.
    // When that is done ONE of the following should also be done: 
    // 1) Remove the pragmas that cause the queue's callback functions to be paged
    //    out. Also remove the PAGED_CODE macros from the functions.
    // 2) If you would like to have the callbacks pageable, then set
    //    the WDF_EXECUTION_LEVEL enum (member of WDF_OBJECT_ATTRIBUTES)
    //    to WdfExecutionLevelPassive.
    // 
    status = WdfIoQueueCreate(
                 Device,
                 &queueConfig,
                 WDF_NO_OBJECT_ATTRIBUTES,
                 &queue
                 );

    if( !NT_SUCCESS(status) ) {
        KdPrint(("WdfIoQueueCreate failed 0x%x\n",status));
        return status;
    }

    return status;
}

VOID
PortIOEvtIoDeviceControl(
    __in WDFQUEUE     Queue,
    __in WDFREQUEST   Request,
    __in size_t       OutputBufferLength,
    __in size_t       InputBufferLength,
    __in ULONG        IoControlCode
    )
{

    PDEVICE_CONTEXT devContext = NULL;
    WDFDEVICE device;
    NTSTATUS status;

	PFILE_OBJECT  FileObject;
    PDEVICE_OBJECT  DeviceObject;
	PARALLEL_PORT_INFORMATION ParPortInfo;

	UNICODE_STRING parport;
	KEVENT                      ev; 
	PIRP                        irp;
	IO_STATUS_BLOCK             ioStatus;  

	WDFMEMORY  WdfBuffer = NULL;
	PUCHAR     buffer = NULL;

	LPCWSTR				LPT1 = L"\\Device\\ParallelPort0";
	LPCWSTR				LPT2 = L"\\Device\\ParallelPort1";
	LPCWSTR				LPT3 = L"\\Device\\ParallelPort2";
	LPCWSTR				LPT = L"";

    PAGED_CODE();
    
    device = WdfIoQueueGetDevice(Queue);
    
    devContext = PortIOGetDeviceContext(device);
    
    switch(IoControlCode){
        case IOCTL_PRESTI_SEND_BYTE:
			DbgPrint("IOCTL_PRESTI_SEND_BYTE");
			if(InputBufferLength != 2) {
				status = STATUS_INVALID_PARAMETER;
				DbgPrint("IOCTL_PRESTI_SEND_BYTE buffer wrong size");
				break;
			}
			status = WdfRequestRetrieveInputMemory(Request, &WdfBuffer);
			if(status != STATUS_SUCCESS) {
				DbgPrint("WdfRequestRetrieveInputMemory Error", status);
				break;
			}
			buffer = WdfMemoryGetBuffer(WdfBuffer, NULL);
			switch(buffer[0]) {
				case 1: LPT = LPT1; break;
				case 2: LPT = LPT2; break;
				case 3: LPT = LPT3;
			}
			RtlInitUnicodeString(&parport, LPT);
			status = IoGetDeviceObjectPointer(&parport, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
			if(status != STATUS_SUCCESS) {
				DbgPrint("1 - %X%", status);
				IoDeleteDevice(DeviceObject);
				break;
			}
			ObReferenceObjectByPointer(DeviceObject, FILE_READ_ATTRIBUTES, NULL, KernelMode);
			ObDereferenceObject(FileObject);

			// PARALLEL PORT INFO
			KeInitializeEvent(&ev, NotificationEvent, FALSE); 

			irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO, DeviceObject, NULL, 0, &ParPortInfo, sizeof(PARALLEL_PORT_INFORMATION), TRUE, &ev, &ioStatus);
			if (!irp) {
				DbgPrint( ("IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO Insufficent Resources for IoBuildDeviceIoControlRequest") );
				IoDeleteDevice(DeviceObject);
				break;
			}

			status = IoCallDriver(DeviceObject, irp);
			if(status != STATUS_SUCCESS) {
				DbgPrint("2 - %X%", status);
				IoDeleteDevice(DeviceObject); 
				break;
			}

			status = KeWaitForSingleObject(&ev, Executive, KernelMode, FALSE, NULL);
			if(status != STATUS_SUCCESS) {
				DbgPrint("3 - %X%", status);
				IoDeleteDevice(DeviceObject);
				break;
			}
			DbgPrint("Parallel Port at Address 0x%x and spans %d bytes",ParPortInfo.Controller,ParPortInfo.SpanOfController); 

			// ALLOCATE
			KeInitializeEvent(&ev, NotificationEvent, FALSE); 

			irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE, DeviceObject, NULL, 0, NULL, 0, TRUE, &ev, &ioStatus);
			if (!irp) {
				DbgPrint("IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE Insufficent Resources for IoBuildDeviceIoControlRequest");
				IoDeleteDevice(DeviceObject);
				break;
			}

			status = IoCallDriver(DeviceObject, irp);
			if(status != STATUS_SUCCESS) {
				DbgPrint("2b - %X%", status);
				IoDeleteDevice(DeviceObject); 
				break;
			}

			status = KeWaitForSingleObject(&ev, Executive, KernelMode, FALSE, NULL);
			if(status != STATUS_SUCCESS) {
				DbgPrint("3b - %X%", status);
				IoDeleteDevice(DeviceObject);
				break;
			}

			//ByteOut(11, (ULONG)ParPortInfo.Controller);
			
			outportb((ULONG)ParPortInfo.Controller, buffer[1]);

			// FREE
			KeInitializeEvent(&ev, NotificationEvent, FALSE); 

			irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_PARALLEL_PORT_FREE, DeviceObject, NULL, 0, NULL, 0, TRUE, &ev, &ioStatus);
			if (!irp) {
				DbgPrint("IOCTL_INTERNAL_PARALLEL_PORT_FREE Insufficent Resources for IoBuildDeviceIoControlRequest");
				IoDeleteDevice(DeviceObject);
				break;
			}

			status = IoCallDriver(DeviceObject, irp);
			if(status != STATUS_SUCCESS) {
				DbgPrint("2c - %X%", status);
				IoDeleteDevice(DeviceObject); 
				break;
			}

			status = KeWaitForSingleObject(&ev, Executive, KernelMode, FALSE, NULL);
			if(status != STATUS_SUCCESS) {
				DbgPrint("3c - %X%", status);
				IoDeleteDevice(DeviceObject);
				break;
			}


			ObDereferenceObject(DeviceObject);
			status = STATUS_SUCCESS;
            break;


        default:
            status = STATUS_INVALID_PARAMETER;
            WdfRequestComplete(Request, status);
			goto exit;
            break;

                               
    }

	WdfRequestCompleteWithInformation(Request, status, 10);
    return;
    
	exit:
		WdfRequestComplete(Request, status);
		return;
        
}